﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;

namespace Metro.Kashikoi.Service.Common.Entity
{
    /// <summary>
    /// Repository.
    /// </summary>
    /// <typeparam name="T">The entity type.</typeparam>
    public class Repository<T> : IRepository<T> where T : class
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Repository&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="ctx">The CTX.</param>
        public Repository(IUnitOfWork ctx)
        {
            this.Context = ctx;
        }

        #endregion

        #region Properties

        public IUnitOfWork Context { get; set; }

        IQueryable<T> _objectSet;
        private IQueryable<T> ObjectSet
        {
            get
            {
                if (_objectSet == null)
                {
                    _objectSet = Context.GetQuery<T>();
                }
                return _objectSet;
            }
        }

        #endregion

        #region IRepository<T> Members

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.GetQuery"/>
        /// </summary>
        public IQueryable<T> GetQuery()
        {
            return this.ObjectSet;
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Where"/>
        /// </summary>
        public IEnumerable<T> Where(Func<T, bool> predicate)
        {
            return this.ObjectSet.Where(predicate);
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Where"/>
        /// </summary>
        public IEnumerable<TResult> Where<TResult>(Func<TResult, bool> predicate) where TResult : T
        {
            return this.ObjectSet.OfType<TResult>().Where(predicate);
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Single"/>
        /// </summary>
        public T Single(Func<T, bool> predicate)
        {
            return this.ObjectSet.Single<T>(predicate);
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Single"/>
        /// </summary>
        public TResult Single<TResult>(Func<TResult, bool> predicate) where TResult : T
        {
            return this.ObjectSet.OfType<TResult>().Single(predicate);
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Count"/>
        /// </summary>
        public int Count(Func<T, bool> predicate)
        {
            return this.ObjectSet.Count(predicate);
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Count"/>
        /// </summary>
        public int Count<TResult>(Func<TResult, bool> predicate) where TResult : T
        {
            return this.ObjectSet.OfType<TResult>().Count(predicate);
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Delete"/>
        /// </summary>
        public void Delete(T entity)
        {
            this.Context.Delete(entity);
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Add"/>
        /// </summary>
        public void Add(T entity)
        {
            this.Context.Add(entity);
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Attach"/>
        /// </summary>
        public void Attach(T entity)
        {
            this.Context.Attach(entity);
        }

        /// <summary>
        /// <see cref="IRepository&lt;T&gt;.Remove"/>
        /// </summary>
        public void Remove(T entity)
        {
            this.Context.Remove(entity);
        }

        #endregion
    }
}
